package cn.tedu.charging.order.service.impl;

import cn.tedu.charging.common.constant.MqttTopicConst;
import cn.tedu.charging.common.pojo.message.DelayCheckMessage;
import cn.tedu.charging.common.pojo.message.StartCheckMessage;
import cn.tedu.charging.common.pojo.param.OrderAddParam;
import cn.tedu.charging.common.protocol.JsonResult;
import cn.tedu.charging.common.protocol.WebSocketResult;
import cn.tedu.charging.common.utils.CronUtil;
import cn.tedu.charging.common.utils.SnowflakeIdGenerator;
import cn.tedu.charging.common.utils.XxlJobTaskUtil;
import cn.tedu.charging.order.amqp.AmqpDelayProducer;
import cn.tedu.charging.order.clients.DeviceClient;
import cn.tedu.charging.order.clients.UserClient;
import cn.tedu.charging.order.dao.repository.BillRepository;
import cn.tedu.charging.order.dao.repository.impl.BillRepositoryImpl;
import cn.tedu.charging.order.mqtt.MqttProducer;
import cn.tedu.charging.order.points.WebsocketServerPoint;
import cn.tedu.charging.order.pojo.po.ChargingBillSuccessPO;
import cn.tedu.charging.order.service.OrderService;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
    @Autowired
    private DeviceClient deviceClient;
    @Autowired
    private UserClient userClient;
    @Autowired
    private SnowflakeIdGenerator snowflakeIdGenerator;
    @Autowired
    private MqttProducer mqttProducer;
    @Autowired
    private AmqpDelayProducer delayProducer;
    @Autowired
    private BillRepository billRepository;
    //将能够推送消息的WebSocketServerpoint注入
    @Autowired
    private WebsocketServerPoint wsServerPoint;
    @Override
    public String createOrder(OrderAddParam param) {
        //1.检查设备---轻做
        checkGunAvailable(param.getGunId());
        // 2.检车车主或者车辆--轻做
        checkUserAvailable(param.getUserId(),param.getGunId());
        //3.生成订单编号
        String billId=snowflakeIdGenerator.nextId()+"";
        //4.和设备通信,发送开始充电命令
        //4.1组织一个消息对象 StartCheckMessage
        StartCheckMessage message=new StartCheckMessage();
        message.setOrderNo(billId);
        message.setUserId(param.getUserId());
        message.setGunId(param.getGunId());
        String msg= JSON.toJSONString( message);
        //4.2发送消息
        mqttProducer.send(
                MqttTopicConst.START_GUN_CHECK_PREFIX+param.getPileId(),msg);
        //5.发送延迟消息 防止设备没有响应
        //5.1组织消息对象 转化json
        DelayCheckMessage delayMessage=new DelayCheckMessage();
        BeanUtils.copyProperties(param,delayMessage);
        delayMessage.setOrderNo(billId);
        String delayMsg=JSON.toJSONString(delayMessage);
        //5.2发送消息
        delayProducer.sendDelay("delay_ex",delayMsg,1000*60);
        //TODO 6.修改枪状态
        //7.计算该订单的最长执行充电时间 发布定时任务
        //7.1 确定任务的创建入参 时间cron表达式 订单执行器 订单编号
        //7.2 理论上 cron计算要根据订单的不同类型参数 获取最大预估充电时长,定时时间要大于这个预估充电时长
        //这里作为测试流程步骤,时间定义在2分钟(比1分钟延迟长一点)
        XxlJobTaskUtil.createJobTask(CronUtil.delayCron(1000*60*2),"order-executor",billId);
        return billId;
    }

    @Override
    public void orderStatusCheck(String billId) {
        //1.调用仓储层获取success订单
        ChargingBillSuccessPO successPO=billRepository.selectSuccessByBillId(billId);
        //判断存在
        if (successPO!=null){
            //判断状态是否依然是1
            if (successPO.getBillStatus()!=null&&successPO.getBillStatus()==1){
                log.debug("订单:{},到达最大充电时长,状态依然正在充电,异常",billId);
                //2.修改订单状态为3
                billRepository.updateSuccessStatus(successPO.getId(),3);
                //3.组织异常单,写入数据库
                billRepository.saveExceptionBill(successPO);
                //4.通知用户 订单异常结束请拔枪走人
                WebSocketResult<String> userMsg=new WebSocketResult<>();
                userMsg.setState(1);
                userMsg.setMessage("订单异常");
                userMsg.setData("您的订单充电过程异常,请结账结束,送你一张优惠券https://9g9g9G9B");
                try{
                    wsServerPoint.pushMsg(JSON.toJSONString(userMsg), successPO.getUserId());
                }catch (Exception e){
                    log.error("告知用户订单异常,userId:{},消息:{}",successPO.getUserId(),JSON.toJSONString(userMsg),e);
                }
            }else{
                log.debug("订单:{},状态已结束,无需检查异常结束",billId);
            }
        }else{
            log.debug("订单:{},没有对应的成功订单,无需检查异常结束",billId);
        }
    }

    private void checkUserAvailable(Integer userId, Integer gunId) {
        //1.调用车主服务
        JsonResult<Boolean> result=userClient.checkUserGun(userId,gunId);
        //2.解析结果
        Boolean available=result.getData();
        if (available){
            log.debug("车主检查枪可用,userId={},gunId={}",userId,gunId);
        }else{
            log.debug("车主检查枪不可用,userId={},gunId={}",userId,gunId);
            throw new RuntimeException("车主不可使用该枪充电");
        }
    }

    private void checkGunAvailable(Integer gunId) {
        //1.调用deviceClient
        JsonResult<Boolean> result=deviceClient.checkGun(gunId);
        //2.解析结果
        Boolean available=result.getData();
        if (available){
            log.debug("设备检查枪可用,gunId={}",gunId);
        }else{
            log.debug("设备检查枪不可用,gunId={}",gunId);
            throw new RuntimeException("设备检查枪不可用");
        }
    }
}
